package com.muyu.util;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import static com.muyu.util.EasypoiUtils.HttpHeaderConstants.*;

/**
 * @ClassName: EasypoiUtils
 * @Author: Zirun.Xin
 * @Date: 2025/4/11 上午9:48
 * @Version: 1.0
 * @Description: easypoi组件工具
 */
public class EasypoiUtils {

    /**
     * 导入Excel文件数据并转换为指定类型的对象列表
     *
     * @param <T>         目标对象的类型参数
     * @param inputStream Excel文件输入流（自动关闭，不可为空）
     * @param sheetIndex  要读取的工作表索引(0-based)
     * @param titleRows   标题行数(0=无标题行，1=有标题行)
     * @param headerRows  表头行数(通常为1，表示列名所在行)
     * @param pojoClass   目标对象的Class类型（不可为空）
     * @return 包含解析后的数据对象列表（永不为null，空列表表示无有效数据）
     * @throws IllegalArgumentException 如果参数不合法
     * @throws ExcelImportException     Excel解析或数据转换专用异常
     * @throws IOException              读取文件时发生的IO异常
     * @implSpec 1. 自动关闭输入流
     * 2. 空文件返回空集合
     * 3. 自动跳过空白行
     * @example 基础用法示例：
     * try (InputStream is = Files.newInputStream(Paths.get("data.xlsx"))) {
     * List<Employee> employees = importExcel(is, 0, 1, 1, Employee.class);
     * }
     * @see ImportParams
     * @see ExcelImportUtil
     */
    public static <T> List<T> importExcel(InputStream inputStream, int sheetIndex,
                                          Integer titleRows, Integer headerRows, Class<T> pojoClass)
            throws IOException, ExcelImportException {
        // 参数校验
        Objects.requireNonNull(inputStream, "输入流不能为空");
        Objects.requireNonNull(pojoClass, "目标类型不能为空");
        if (sheetIndex < 0) {
            throw new IllegalArgumentException("工作表索引不能为负数");
        }

        // 根据file得到Workbook,主要是要根据这个对象获取,传过来的excel有几个sheet页
        ImportParams params = new ImportParams();
        // 第几个sheet表页
        params.setStartSheetIndex(sheetIndex);
        // 设置表标题行数
        params.setTitleRows(Optional.ofNullable(titleRows).orElse(0));
        // 设置表头行数
        params.setHeadRows(Optional.ofNullable(headerRows).orElse(1));

        try {
            // 读取excel数据的集合并返回
            return ExcelImportUtil.importExcel(inputStream, pojoClass, params);
        } catch (ExcelImportException e) {
            throw new ExcelImportException("Excel数据解析失败: " + e.getMessage(), e);
        } catch (Exception e) {
            throw new IOException("读取Excel文件失败", e);
        } finally {
            inputStream.close();
        }
    }

    /**
     * Excel 导出工具方法
     *
     * @param <T>        导出的数据类型
     * @param response   HttpServletResponse 对象
     * @param sheetName  Excel 工作表名称
     * @param sheetTitle Excel 标题（可选）
     * @param fileName   下载文件名（不带扩展名）
     * @param pojoClass  数据对象类型
     * @param data       要导出的数据列表
     * @throws IOException 当 IO 操作失败时抛出
     * @implNote 默认使用 .xlsx 格式导出
     * @see ExcelExportUtil
     * @see ExportParams
     */
    public static <T> void exportExcel(HttpServletResponse response, String sheetName, String sheetTitle,
                                       String fileName, Class<T> pojoClass, List<T> data) throws IOException {
        // 参数校验
        Objects.requireNonNull(response, "HttpServletResponse 不能为空");
        Objects.requireNonNull(pojoClass, "目标类类型不能为空");

        // 设置excel参数
        ExportParams params = new ExportParams();
        // 设置sheet名
        params.setSheetName(sheetName);
        // 设置标题
        params.setTitle(StringUtils.isBlank(sheetTitle) ? null : sheetTitle);

        // 设置信息头，告诉浏览器内容为excel类型
        response.setHeader(CONTENT_TYPE_HEADER, EXCEL_XLSX_MIME_TYPE);
        // 设置下载名称
        response.setHeader(
                CONTENT_DISPOSITION_HEADER,
                String.format(ATTACHMENT_FILENAME_FORMAT, encodeFilename(fileName + EXCEL_XLSX_EXTENSION))
        );

        // 执行导出
        try (Workbook workbook = ExcelExportUtil.exportExcel(params, pojoClass, data);
             ServletOutputStream out = response.getOutputStream()) {
            workbook.write(out);
        }
    }

    /**
     * 编码文件名（处理特殊字符）
     */
    private static String encodeFilename(String filename) throws UnsupportedEncodingException {
        return URLEncoder.encode(filename, StandardCharsets.UTF_8.name())
                .replaceAll("\\+", "%20");
    }

    /**
     * Excel导入专用异常（业务异常）
     */
    public static class ExcelImportException extends Exception {
        public ExcelImportException(String message) {
            super(message);
        }

        public ExcelImportException(String message, Throwable cause) {
            super(message, cause);
        }
    }

    /**
     * HTTP 响应头相关常量
     */
    public static final class HttpHeaderConstants {

        /**
         * Excel文件的标准MIME类型 (.xls)
         *
         * @see #EXCEL_XLSX_MIME_TYPE
         */
        public static final String EXCEL_MIME_TYPE = "application/vnd.ms-excel";

        /**
         * Office Open XML 格式的MIME类型 (.xlsx)
         */
        public static final String EXCEL_XLSX_MIME_TYPE =
                "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";

        /**
         * Content-Type 响应头
         */
        public static final String CONTENT_TYPE_HEADER = "Content-Type";

        /**
         * Content-Disposition 响应头
         */
        public static final String CONTENT_DISPOSITION_HEADER = "Content-Disposition";

        /**
         * 附件下载的文件名格式模板
         */
        public static final String ATTACHMENT_FILENAME_FORMAT = "attachment;filename=\"%s\"";

        /**
         * 传统二进制格式的 Excel 文件扩展名 (.xls)
         */
        public static final String EXCEL_XLS_EXTENSION = ".xls";

        /**
         * Office Open XML 格式的 Excel 文件扩展名 (.xlsx)
         */
        public static final String EXCEL_XLSX_EXTENSION = ".xlsx";

        /**
         * 防止实例化
         */
        private HttpHeaderConstants() {

        }
    }
}
